package ca.clarity.hms.util;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Handler;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.InvalidParameterException;

/**
 * 마이크를 통해 수집 한 음성을 MP3로 저장
 * <p/>
 * 다른 스레드에서 마이크를 통해 녹음, MP3로 변환 할
 */
public class RecMicToMp3 {

    static {
        System.loadLibrary("mp3lame");
    }

    /**
     * MP3 파일을 저장할 파일 경로
     */
    private String mFilePath;

    /**
     * 샘플링 속도
     */
    private int mSampleRate;

    /**
     * 녹음 중인가
     */
    private boolean mIsRecording = false;

    /**
     * 일시정지했는지 여부. 일시정지 상태에서는 true 로 바뀌어 음성이 파일에 저장되지 않고, 녹음 재개시 다시 false 로 바뀌어 파일에 저장 된다
     */
    private boolean mIsPaused = false;

    /**
     * 녹음 상태 변화를 알려주기 핸들러 //録音の状態変化を通知するハンドラ
     *
     * @see RecMicToMp3#MSG_REC_STARTED
     * @see RecMicToMp3#MSG_REC_STOPPED
     * @see RecMicToMp3#MSG_ERROR_GET_MIN_BUFFERSIZE
     * @see RecMicToMp3#MSG_ERROR_CREATE_FILE
     * @see RecMicToMp3#MSG_ERROR_REC_START
     * @see RecMicToMp3#MSG_ERROR_AUDIO_RECORD
     * @see RecMicToMp3#MSG_ERROR_AUDIO_ENCODE
     * @see RecMicToMp3#MSG_ERROR_WRITE_FILE
     * @see RecMicToMp3#MSG_ERROR_CLOSE_FILE
     */
    private Handler mHandler;

    /**
     * 녹음을 시작했다
     */
    public static final int MSG_REC_STARTED = 0;

    /**
     * 녹음이 끝난
     */
    public static final int MSG_REC_STOPPED = 1;

    /**
     * 버퍼 크기를 가져올 수 없습니다. 샘플링 레이트 등의 설정을 단말기가 지원하지 않을 수있다.
     */
    public static final int MSG_ERROR_GET_MIN_BUFFERSIZE = 2;

    /**
     * 파일을 생성 할 수없는 // ファイルが生成できない
     */
    public static final int MSG_ERROR_CREATE_FILE = 3;

    /**
     * 녹음 시작 실패
     */
    public static final int MSG_ERROR_REC_START = 4;

    /**
     * 녹음 수 없다. 녹음 중에 시작 후에 만 발행한다.
     */
    public static final int MSG_ERROR_AUDIO_RECORD = 5;

    /**
     * 인코딩에 실패했다. 녹음 중에 시작 후에 만 발행한다.
     */
    public static final int MSG_ERROR_AUDIO_ENCODE = 6;

    /**
     * 파일 내보내기에 실패했다. 녹음 중에 시작 후에 만 발행한다.
     */
    public static final int MSG_ERROR_WRITE_FILE = 7;

    /**
     * 파일 닫기에 실패했다. 녹음 중에 시작 후에 만 발행한다.
     */
    public static final int MSG_ERROR_CLOSE_FILE = 8;

    /**
     * 생성자
     *
     * @param filePath   저장할 파일 경로
     * @param sampleRate 녹음 샘플링 속도
     */
    public RecMicToMp3(String filePath, int sampleRate) {
        if (sampleRate <= 0) {
            throw new InvalidParameterException(
                    "Invalid sample rate specified.");
        }
        this.mFilePath = filePath;
        this.mSampleRate = sampleRate;
    }

    /**
     * 녹음을 시작하려면
     */
    public void start() {
        //   녹음의 경우는 아무것도하지
        if (mIsRecording) {
            return;
        }

        //  녹음을 별도의 스레드에서 시작
        new Thread() {
            @Override
            public void run() {
                android.os.Process
                        .setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
                //  최소 버퍼 크기
                final int minBufferSize = AudioRecord.getMinBufferSize(
                        mSampleRate, AudioFormat.CHANNEL_IN_MONO,
                        AudioFormat.ENCODING_PCM_16BIT);
                //  버퍼 크기를 가져올 수 없습니다. 샘플링 레이트 등의 설정을 단말기가 지원하지 않을 수있다.
                if (minBufferSize < 0) {
                    if (mHandler != null) {
                        mHandler.sendEmptyMessage(MSG_ERROR_GET_MIN_BUFFERSIZE);
                    }
                    return;
                }
                // getMinBufferSize 에서 얻은 값의 경우 //  getMinBufferSize
                // "W/AudioFlinger(75): RecordThread: buffer overflow"   //가 발생하도록이기 때문에 약간 큰 값으로하고있다 //
                AudioRecord audioRecord = new AudioRecord(
                        MediaRecorder.AudioSource.MIC, mSampleRate,
                        AudioFormat.CHANNEL_IN_MONO,
                        AudioFormat.ENCODING_PCM_16BIT, minBufferSize * 2);

                // PCM buffer size (5sec)
                short[] buffer = new short[mSampleRate * (16 / 8) * 1 * 5]; // SampleRate[Hz] * 16bit * Mono * 5sec
                byte[] mp3buffer = new byte[(int) (7200 + buffer.length * 2 * 1.25)];

                FileOutputStream output = null;
                try {
                    output = new FileOutputStream(new File(mFilePath));
                } catch (FileNotFoundException e) {
                    // 파일을 생성 할 수없는
                    if (mHandler != null) {
                        mHandler.sendEmptyMessage(MSG_ERROR_CREATE_FILE);
                    }
                    return;
                }

                // Lame init
                SimpleLame.init(mSampleRate, 1, mSampleRate, 32);

                mIsRecording = true; // 녹음 시작 신고
                try {
                    try {
                        audioRecord.startRecording(); //녹음을 시작하려면
                    } catch (IllegalStateException e) {
                        // 녹음 시작 실패
                        if (mHandler != null) {
                            mHandler.sendEmptyMessage(MSG_ERROR_REC_START);
                        }
                        return;
                    }

                    try {
                        // 녹음을 시작했다
                        if (mHandler != null) {
                            mHandler.sendEmptyMessage(MSG_REC_STARTED);
                        }

                        int readSize = 0;
                        while (mIsRecording) {

                            if (mIsPaused) {
                                // 일시 정지 때는 아무 동작도 하지 않는다..
                            } else {
                                readSize = audioRecord.read(buffer, 0, minBufferSize);
                                if (readSize < 0) {
                                    // 녹음 수 없다
                                    if (mHandler != null) {
                                        mHandler.sendEmptyMessage(MSG_ERROR_AUDIO_RECORD);
                                    }
                                    break;
                                }
                                // 데이터를로드 할 수없는 경우는 아무것도하지
                                else if (readSize == 0) {
                                    ;
                                }
                                // 데이터가 들어있는 경우
                                else {
                                    int encResult = SimpleLame.encode(buffer,
                                            buffer, readSize, mp3buffer);
                                    if (encResult < 0) {
                                        // 인코딩 실패
                                        if (mHandler != null) {
                                            mHandler.sendEmptyMessage(MSG_ERROR_AUDIO_ENCODE);
                                        }
                                        break;
                                    }
                                    if (encResult != 0) {
                                        try {
                                            output.write(mp3buffer, 0, encResult);
                                        } catch (IOException e) {
                                            // 파일 내보내기에 실패
                                            if (mHandler != null) {
                                                mHandler.sendEmptyMessage(MSG_ERROR_WRITE_FILE);
                                            }
                                            break;
                                        }
                                    }
                                }
                            }    // 일시 정지가 아닌 경우
                        }    // while 문 종료. (stop 버튼이 눌린 경우)

						/* stop 이후 */

                        int flushResult = SimpleLame.flush(mp3buffer);
                        if (flushResult < 0) {
                            // 인코딩 실패
                            if (mHandler != null) {
                                mHandler.sendEmptyMessage(MSG_ERROR_AUDIO_ENCODE);
                            }
                        }
                        if (flushResult != 0) {
                            try {
                                output.write(mp3buffer, 0, flushResult);
                            } catch (IOException e) {
                                // 파일 내보내기에 실패
                                if (mHandler != null) {
                                    mHandler.sendEmptyMessage(MSG_ERROR_WRITE_FILE);
                                }
                            }
                        }

                        try {
                            output.close();
                        } catch (IOException e) {
                            // 파일 닫기 실패
                            if (mHandler != null) {
                                mHandler.sendEmptyMessage(MSG_ERROR_CLOSE_FILE);
                            }
                        }
                    } finally {
                        audioRecord.stop(); //녹음을 중지하려면
                        audioRecord.release();
                    }
                } finally {
                    SimpleLame.close();
                    mIsRecording = false; //녹음 시작 플래그를 낮추는
                }

                // 녹음이 끝난
                if (mHandler != null) {
                    mHandler.sendEmptyMessage(MSG_REC_STOPPED);
                }
            }
        }.start();
    }

    /**
     * 녹음을 중지하려면
     */
    public void stop() {
        mIsRecording = false;
    }

    /**
     * 녹음을 일시중지하려면
     */
    public void pause() {
        mIsPaused = true;
    }

    /**
     * 녹음을 재개하려면
     */
    public void resume() {
        mIsPaused = false;
    }


    /**
     * 녹음 중 하나를 얻을
     *
     * @return true의 경우는 녹음 중, 그렇지 않으면 false
     */
    public boolean isRecording() {
        return mIsRecording;
    }

    /**
     * 녹음 상태 변화를 알려주기 핸들러를 설정하는
     *
     * @param handler 녹음 상태 변화를 알려주기 핸들러
     * @see RecMicToMp3#MSG_REC_STARTED
     * @see RecMicToMp3#MSG_REC_STOPPED
     * @see RecMicToMp3#MSG_ERROR_GET_MIN_BUFFERSIZE
     * @see RecMicToMp3#MSG_ERROR_CREATE_FILE
     * @see RecMicToMp3#MSG_ERROR_REC_START
     * @see RecMicToMp3#MSG_ERROR_AUDIO_RECORD
     * @see RecMicToMp3#MSG_ERROR_AUDIO_ENCODE
     * @see RecMicToMp3#MSG_ERROR_WRITE_FILE
     * @see RecMicToMp3#MSG_ERROR_CLOSE_FILE
     */
    public void setHandle(Handler handler) {
        this.mHandler = handler;
    }

    public boolean isPuased() {
        return mIsPaused;
    }

}
